#include "network.h"

// TCP、UDP初始化封装函数,返回值为套接字
// 1) 当是服务端初始化时,ipAddr:地址可以为空,port:端口号一定不能为NULL
// 2) setTcpUdpMode: TCP_FLAG,TCP协议; UDP_FLAG,UDP协议
// 3) setTcpUdpMode:设置TCP_FLAG,TCP协议;设置DCP_FLAG,DCP协议
// 4) 服务端初始化:返回服务端套接字; 客户端初始化:返回客户端套接字
int socketInit(char* ipAddr, int* port, int setTcpUdpMode, int setSerCidMode)
{

    // 判断TCP和UDP是否传入正确的参数
    if(ipAddr == NULL && port != NULL){
        if(setSerCidMode != SER_FLAG){ // 当是服务器初始化时,ip地址可以为空
            printf("没有输入IP地址");
            DEBUGERROR;
            return 0; //当没有值输入时,返回0,表示NULL
        }
    }else if(ipAddr != NULL && port == NULL){
        printf("没有输入端口");
        DEBUGERROR;
        return 0; //当没有值输入时,返回0,表示NULL
    }else if(ipAddr == NULL && port == NULL){
        printf("没有输入端口和IP都没有输入");
        DEBUGERROR;
        return 0; //当没有值输入时,返回0,表示NULL
    }

    // 创建套接字
    int sockfd;
    if(setTcpUdpMode == TCP_FLAG){
        sockfd = socket(AF_INET, SOCK_STREAM, 0); // tcp套接字生成
    }else if(setTcpUdpMode == UDP_FLAG){
        sockfd = socket(AF_INET, SOCK_DGRAM, 0); // udp套接字生成
    }

    if(sockfd < 0){  // 判断套接字是否建立成功
        printf("socket");
        DEBUGERROR;
        close(sockfd);
        return -1; // 当出现错误时,返回为-1
    }

    // 设置端口复用
    int reOpt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reOpt, sizeof(reOpt));

    // TCP、UDP相关结构体初始化
    // 使用该写法,防止同一进程调用两次
    if(setSerCidMode == SER_FLAG){ // 服务端结构体数据初始化,设置本机的地址
        memset(&serSockAddr, 0, sizeof(serSockAddr));
        serSockAddr.sin_family = AF_INET; // 设置协议
        serSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        serSockAddr.sin_port = htons(*port);  // 设置端口号
    }else if(setSerCidMode == CLI_FLAG){ // 客户端设置,设置服务器的地址和端口
        memset(&cliSockAddr, 0, sizeof(cliSockAddr));
        inet_pton(AF_INET, ipAddr, &(cliSockAddr.sin_addr.s_addr));
        cliSockAddr.sin_family = AF_INET; // 设置协议
        cliSockAddr.sin_port = htons(*port);  // 设置端口号
    }
    serSockAddrLen = sizeof(serSockAddr);
    cliSockAddrLen = sizeof(cliSockAddr);

    // 是服务端,绑定地址
    if(setSerCidMode == SER_FLAG){
        // 绑定IP地址.UDP服务端,仅需要使用bind函数进行绑定就行,客户端不需要其他操作
        if(bind(sockfd, (SA*)&serSockAddr, serSockAddrLen) != 0){
            printf("bind");
            DEBUGERROR;
            return -1;
        }
    }

    // TCP相关的操作
    if(setTcpUdpMode == TCP_FLAG && setSerCidMode == SER_FLAG){   //  TCP服务端设置
        // 设置监听个数
        if(listen(sockfd, listenMax) != 0){
            printf("listen");
            DEBUGERROR;
            return -1;
        }
    }else if(setTcpUdpMode == TCP_FLAG && setSerCidMode == CLI_FLAG) { //  TCP客户端设置,连接服务端地址和端口
        if (connect(sockfd, (SA*)&cliSockAddr, cliSockAddrLen) == -1) {
            printf("connect: %d", errno);
            DEBUGERROR;
            return -1;
        }
    }
    // 返回初始化的套接字
    return sockfd;
} 

// TCP、UDP服务端封装函数,返回值为套接字
// 1) 用于接受数据,其中套接字需要主函数自己关闭
// 2) sockfd:TCP时,为accept提取的套接字
// 3) setTcpUdpMode:设置TCP_FLAG,TCP协议;设置DCP_FLAG,DCP协议
int socketServer(int* sockfd,  char* bufData, int bufDataSize, int setTcpUdpMode)
{
    if(sockfd == NULL){
        printf("没有服务端的套接字");
        DEBUGERROR;
        return 0; //当没有值时,返回0
    }
    // 根据你不同服务器进行数据处理
    if(setTcpUdpMode == TCP_FLAG){ // TCP服务器模式
        // 获取已将链接的套接字
        // cfd = accept(*sockfd, (SA*)&cliAddrInform, &cliAddrInformLen);
        if(*sockfd < 0){
            // 判断退出服务器的条件
            if(errno != ECONNABORTED || errno != EINTR){
                    printf("accpet");
                    DEBUGERROR;
                    return -1; // 错误返回-1;
                }
            }
        // 接受并处理信息
        int ret = recv(*sockfd, bufData, bufDataSize, 0);
   
        if(ret == 0){
            printf("%d: 客户端已关闭\n", *sockfd);
        }else if(ret < 0){
            printf("recv");
            close(*sockfd);
            DEBUGERROR;
            return -1; 
        }
    }else if(setTcpUdpMode == UDP_FLAG){ // UDP服务器模式
        struct sockaddr_in cliAddrInform;
        memset(&cliAddrInform, 0, sizeof(cliAddrInform));
        socklen_t cliAddrInformLen = sizeof(cliAddrInform);

        // 接受数据,并获取客户端的地址和端口
        if(recvfrom(*sockfd, bufData, bufDataSize, 0, (struct sockaddr *)&cliAddrInform, &cliAddrInformLen) < 0){
            printf("Failed to recv data");
            close(*sockfd);
            return -1;
        }
        // 打印接受客户端的信息
        char chiIP[20] = "";
        inet_ntop(AF_INET, &cliAddrInform.sin_addr.s_addr, chiIP, 20);
        printf("客户端:%s:%d\n", chiIP, ntohs(cliAddrInform.sin_port));
    }
 

    return 1;
}

 // TCP、UDP客户端封装函数,返回值为套接字
 // 1) 用于发送数据,其中套接字需要主函数自己关闭
 // 2) serSockfd:服务器的套接字,及目的套接字
 // 3) setTcpUdpMode:设置TCP_FLAG,TCP协议;设置UDP_FLAG,UDP协议;
int socketClient(int* serSockfd, char* bufData, int setTcpUdpMode)
{
    if(serSockfd == NULL){
        printf("没有客户端的套接字");
        DEBUGERROR;
        return 0; //当没有值时,返回0
    }

    if(bufData == NULL){
        printf("没有要发送的数据");
        DEBUGERROR;
        return 0; //当没有值时,返回0
    }

    int numBytesSent;
    if(setTcpUdpMode == TCP_FLAG){ // TCP发送数据
        numBytesSent = send(*serSockfd, bufData, strlen(bufData)+1, 0);
        if (numBytesSent < 0) {
            printf("Failed to send data");
            DEBUGERROR;
            return -1;
        }
    }else if(setTcpUdpMode == UDP_FLAG){
        // UDP给服务端发送数据,需要知道服务端的地址和端口
        // printf("*serSockfd:%d\n", *serSockfd);
        // printf("bufData:%s\n", bufData);
        numBytesSent = sendto(*serSockfd, bufData, strlen(bufData)+1, 0, (SA*)&cliSockAddr, cliSockAddrLen);
        if (numBytesSent < 0) {
            //printf("%d\n", errno);
            printf("Failed to send data");
            DEBUGERROR;
            return -1;
        }
    }

    return 1;
}



